import { Component, Input, OnInit, ViewChild, OnChanges, SimpleChanges, ElementRef } from '@angular/core';
import { NzMessageService } from "ng-zorro-antd";
import { FcsService } from "../../../../service/fcs.service";
import { CodeService } from "../../../../service/code.service";
import { CommonService } from "../../../../service/common.service";
import { UploadParam, InvokeResult, Function } from "../../../../shared-component/class/instance";
import { NziModalService } from 'ng-zorro-iop';
import { DomSanitizer } from '@angular/platform-browser';
import { NgZone } from '@angular/core';

@Component({
  selector: 'app-function-code',
  templateUrl: './function-code.component.html',
  styleUrls: ['./function-code.component.less']
})
export class FunctionCodeComponent implements OnInit {
  // 在线编辑器 iframe 对象
  @ViewChild('codeEditor', { static: false }) codeEditor: ElementRef;
  // 函数基本信息
  @Input() fcsInstance: Function;
  @Input() versionName = 'LATEST';

  // 代码上传方式
  uploadType = null;
  uploadTypeList = [];

  // vscode ide iframe url
  ideUrl = null;


  localCode = null;
  ossUrl = '';
  fileMaxSize = 50;
  uploadFileName = '';
  uploadFiles = [];
  isZipFile: boolean = true;
  isJarFile: boolean = true;
  exceedFileMaxSize: boolean = false;
  focusFileInput: boolean = false;
  isLoadingSaveCode = false; // 是否正在上传代码
  isEnabledSaveCode = false; // 是否有需要更新的代码
  config;
  isCodeLoading: boolean = false;
  ossUrlValidator: boolean = true; //验证ossUrl是否通过
  ossBucket: string;
  ossBucketList = [];
  ossFileValidator: boolean = true;
  ossFile: string;
  isLoadingBucket: boolean = false;
  runningMsgId; //点击执行，提示信息的id
  zipPlace: string = '选择上传文件，格式为 zip';
  jarPlace: string = '选择上传文件，格式为 zip / jar'
  inputPlaceholder: string = this.zipPlace;

  /*
   * 代码执行区参数 //////////
  */
  isLoadingInvoke = false; // 是否正在执行函数
  cover = false; // 触发事件编辑器显示开关
  eventEditorOptions = {
    language: "json",
    fontFamily: 'Monaco, Menlo, "Ubuntu Mono", Consolas, source-code-pro, monospace;',
    fontSize: "16",
    theme: 'vs'
  }; // 触发事件编辑器配置
  invokeEvent = "{\r\n    \"key\": \"value\"\r\n}"; // 触发事件
  invokeData: InvokeResult; // 函数执行结果
  /*
   * 代码执行区参数 \\\\\\\\\\
  */


  constructor(
    private fcsService: FcsService,
    private msgService: NzMessageService,
    private commonService: CommonService,
    private sanitizer: DomSanitizer,
    private zone: NgZone
  ) { }

  ngOnInit() {
    // 初始化代码上传方式列表和默认代码上传方式
    this.uploadType = "upload";
    this.uploadTypeList.push(...[
      { label: '本地上传', value: 'upload' },
      { label: '从 OSS 上传', value: 'oss' },
    ]);
    if (this.fcsInstance.runtime.indexOf("python") >= 0 || this.fcsInstance.runtime.indexOf("nodejs") >= 0) {
      this.uploadType = "online";
      this.uploadTypeList.unshift({ label: '在线编辑', value: 'online' });
      // 获取代码包的下载链接
      this.ideUrl = this.sanitizer.bypassSecurityTrustResourceUrl("/ifcs/editor/index.html");
      this.getCodeLink();
    }

    if (this.fcsInstance.runtime.indexOf('java8') >= 0) {
      this.inputPlaceholder = this.jarPlace;
    }

    // 设置上传按钮状态
    this.setUploadButtonStatus();
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.fcsInstance && changes.fcsInstance.currentValue && changes.fcsInstance.previousValue) {
      // 切换了版本才执行
      // 刷新函数状态时不执行
      if (changes.fcsInstance.currentValue.versionName != changes.fcsInstance.previousValue.versionName) {
        this.setUploadButtonStatus();
        this.resetFuntionInvokeData();
        if (this.fcsInstance.runtime.indexOf("python") >= 0 || this.fcsInstance.runtime.indexOf("nodejs") >= 0) {
          this.uploadType = "online";
          this.getCodeLink();
        }
      }
    }
  }

  selectTabCode() {
    if (this.fcsInstance.runtime.indexOf("python") >= 0 || this.fcsInstance.runtime.indexOf("nodejs") >= 0) {
      if (this.uploadType === "online") {
        this.getCodeLink();
      }
    }
  }

  // 获取代码包的下载链接
  getCodeLink() {
    this.isCodeLoading = true;
    this.fcsService.getCode(this.fcsInstance.groupId, this.fcsInstance.functionId, this.versionName).subscribe(res => {
      let codeLink = res['downloadLink'];
      // python nodejs
      if (this.fcsInstance.runtime.indexOf("python") >= 0 || this.fcsInstance.runtime.indexOf("nodejs") >= 0) {
        this.checkIdeReady(codeLink);
        
        this.isCodeLoading = false;
      }
      // golang java
      else {
        this.isCodeLoading = false;
      }
    }, err => {
      this.isCodeLoading = false;
      this.isEnabledSaveCode = false;
    })
  }

  // 检查 vscode 加载成功
  // 并且 playground 插件也加载成功
  checkIdeReady(codeLink) {
    setTimeout(() => {
      if (this.codeEditor.nativeElement.contentWindow.executeCommand) {
        this.codeEditor.nativeElement.contentWindow.executeCommand("webPlayground.reloadZip", ""
        ).then(() => this.loadIdeCode(codeLink)
        ).catch(() => this.checkIdeReady(codeLink));
      } else {
        this.checkIdeReady(codeLink);
      }
    }, 200);
  }

  loadIdeCode(codeLink) {
    this.codeEditor.nativeElement.contentWindow.executeCommand("openEditors.closeAll");
    this.codeEditor.nativeElement.contentWindow.executeCommand("webPlayground.reloadZip", codeLink
    ).then(
      success => {
        this.isCodeLoading = false;
        this.zone.run(() => { });
        if (!success) {
          this.msgService.error("代码加载失败");
        } else {
          // 监听 vscode 中的文件变化,间接的通过改变 workspace 配置来实现
          this.codeEditor.nativeElement.contentWindow.onDidChangeConfiguration(e => {
            // 改变了插件配置的值才是真正的改变
            // Ref: vscode source code(src/vs/platform/configuration/common/configuration.tx ConfigurationTarget)
            if (e.source == 1) {
              this.isEnabledSaveCode = true;
              // 强制更新视图
              this.zone.run(() => { });
            }
          });
        }
      }
    ).catch(err => {
      this.isCodeLoading = false;
      this.zone.run(() => { });
      this.msgService.error("代码加载失败");
    });
  }

  // 下载代码包
  downloadCode() {
    const id = this.msgService.loading(`正在下载版本 ${this.versionName} 代码包`, { nzDuration: 0 }).messageId;
    this.fcsService.getCode(this.fcsInstance.groupId, this.fcsInstance.functionId, this.versionName).subscribe(res => {
      this.msgService.remove(id);
      window.open(res['downloadLink']);
    });
  }

  // 设置上传按钮状态
  setUploadButtonStatus() {
    if (this.uploadType === "upload") {
      if (this.localCode) {
        this.isEnabledSaveCode = true;
      } else {
        this.isEnabledSaveCode = false;
      }
    }
    else if (this.uploadType === 'oss') {
      if (this.ossBucket && this.ossFile) {
        this.isEnabledSaveCode = true;
      } else {
        this.isEnabledSaveCode = false;
      }
    }
  }

  // 切换代码上传方式
  uploadTypeChange(event) {
    if (this.uploadType === 'online') {
      this.getCodeLink();
    } else if (this.uploadType === 'oss') {
      this.getOssBuckets();
    }
    this.setUploadButtonStatus();
  }

  // 获取 OSS Buckets 信息
  getOssBuckets() {
    this.isLoadingBucket = true;
    this.fcsService.getOssBucketList(1, 10000).subscribe(res => {
      this.ossBucketList = res.data;
      this.isLoadingBucket = false;
    }, err => {
      this.isLoadingBucket = false;
    })
  }

  ossFileChange(event) {
    this.ossFile = event;
    let oss_file: string = event;
    let pattern = new RegExp('^/{1}(?:([^\\\\\/\:\*\?\"\<\>\|]+[\s]*[^\\\\\/\:\*\?\"\<\>\|]*/))*[^\\\\\/\:\*\?\"\<\>\|]+[\s]*[^\\\\\/\:\*\?\"\<\>\|]*\.zip$');
    if (pattern.test(oss_file)) {
      this.ossFileValidator = true;
      this.isEnabledSaveCode = false;
    } else {
      this.ossFileValidator = false;
      this.isEnabledSaveCode = true;
    }
    if (this.ossFileValidator && this.ossBucket) {
      this.isEnabledSaveCode = true;
    } else {
      this.isEnabledSaveCode = false;
    }
  }

  bucketChange() {
    if (this.ossBucket && this.ossFile && this.ossFileValidator) {
      this.isEnabledSaveCode = true;
    } else {
      this.isEnabledSaveCode = false;
    }
  }

  // 上传函数代码
  async updateCode() {
    this.isLoadingSaveCode = true;

    let code = null;
    switch (this.uploadType) {
      case 'online':
        code = await this.codeEditor.nativeElement.contentWindow.executeCommand("webPlayground.generateZip");
        break;
      case 'upload':
        if (this.localCode instanceof ArrayBuffer) {
          code = this.commonService.transformArrayBufferToBase64(this.localCode);
        } else {
          code = this.localCode;
        }
        break;
    }

    if (this.uploadType === 'oss') {
      this.fcsService.updateOssCode(this.fcsInstance.groupName, this.fcsInstance.functionName, this.versionName, this.ossBucket, this.ossFile, this.fcsInstance.groupId, this.fcsInstance.functionId).subscribe(res => {
        this.msgService.success("代码上传成功");
        this.isLoadingSaveCode = false;
        this.isEnabledSaveCode = false;
      }, err => {
        this.isLoadingSaveCode = false;
      })
    } else {
      this.fcsService.updateCode(this.fcsInstance.groupName, this.fcsInstance.functionName, this.versionName, code, this.fcsInstance.groupId, this.fcsInstance.functionId).subscribe(res => {
        this.msgService.success("代码上传成功");
        this.isLoadingSaveCode = false;
        this.isEnabledSaveCode = false;
      }, err => {
        this.isLoadingSaveCode = false;
      });
    }
  }

  fileNameValidator2(fileName) {
    let name: string = fileName;
    let pattern = new RegExp('^[^\\\\\/\:\*\?\"\<\>\|]+[\s]*[^\\\\\/\:\*\?\"\<\>\|]*\.zip$');
    let pattern2 = new RegExp('^[^\\\\\/\:\*\?\"\<\>\|]+[\s]*[^\\\\\/\:\*\?\"\<\>\|]*\.jar$');
    if (pattern.test(name)) {
      this.isZipFile = true;
    } else {
      this.isZipFile = false;
    }
    if (pattern2.test(name)) {
      this.isJarFile = true;
    } else {
      this.isJarFile = false;
    }
  }

  fileSizeValidator() {
    if (this.uploadFiles.length > 0) { //计算上传文件的大小，每个文件Bite
      let file_size = this.uploadFiles[0].size / 1024 / 1024;
      if (file_size > this.fileMaxSize) {
        this.exceedFileMaxSize = true;
      } else {
        this.exceedFileMaxSize = false;
      }
    }
  }

  focusFileName() {
    this.focusFileInput = true;
  }

  blurFileName() {
    this.focusFileInput = false;
  }

  onUploadFileChange(event) {
    var obj = event.srcElement ? event.srcElement : event.target;
    if (obj.files.length == 0) {
      return false;
    }
    const file = obj.files[0]; // 获取从上传本地文件转换的数据
    this.uploadFileName = file.name;
    this.fileNameValidator2(file.name);
    if (this.fcsInstance.runtime.indexOf('java8') >= 0) {
      if (!this.isZipFile && !this.isJarFile) {
        this.isEnabledSaveCode = false;
        return;
      }
    } else {
      if (!this.isZipFile) {
        this.isEnabledSaveCode = false;
        return;
      }
    }

    this.uploadFiles = obj.files;
    this.fileSizeValidator();
    if (this.exceedFileMaxSize) {
      this.isEnabledSaveCode = false;
      return;
    }
    var size = file.size / 1024 / 1024;
    if (size > this.fileMaxSize) {
      this.msgService.error(`上传文件大小不能大于${this.fileMaxSize}MB！`);
      obj.value = "";
      this.isEnabledSaveCode = false;
      return false;
    }
    this.isEnabledSaveCode = true;
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = (e1) => {
      this.localCode = e1.target['result']; // 获取文件装换后的Base64位文件
      this.localCode = this.localCode.split('base64,')[1];
      this.uploadFileName = file.name;
      this.isEnabledSaveCode = true;
      obj.value = "";
    };
  }

  /*
   * 代码执行区函数
  */
  // 重置函数执行结果
  resetFuntionInvokeData() {
    this.invokeData = {
      log: "--",
      memoryUsage: 0,
      requestId: '--',
      returnCode: 0,
      returnMsg: "--",
      runtimeDuration: 0,
      checksum: "--"
    };
  }
  // 执行函数
  invokeFunction() {
    if (this.invokeEvent == "") {
      return this.msgService.error("触发事件（event）不能为空");
    }
    this.isLoadingInvoke = true;

    this.fcsService.invokeFunction(this.fcsInstance.groupId, this.fcsInstance.functionId, this.versionName, this.invokeEvent).subscribe(res => {
      res.memoryUsage = parseFloat((res.memoryUsage / 1024 / 1024).toFixed(2));
      try {
        let returnMsg = JSON.parse(res.returnMsg);
        res.returnMsg = JSON.stringify(returnMsg, null, 4);
      } catch (err) { }

      this.invokeData = res;
      this.isLoadingInvoke = false;
    }, err => {
      // 执行函数失败
      this.isLoadingInvoke = false;
      let requestId = "--";
      if (err && err.error && err.error.requestId) {
        requestId = err.error.requestId;
      }
      this.invokeData = {
        log: "",
        memoryUsage: 0,
        requestId: requestId,
        returnCode: 500,
        returnMsg: "",
        runtimeDuration: 0,
        checksum: ""
      }
    });
  }
}
